#include <iostream>
#include "event.h"
#include "accept.h"
#include "global.h"
#include "errno.h"
#include "logManager.h"
#include "iniReader.h"
#include "signal.h"
#include "dll.h"

using namespace std;

struct event_base* main_base;  
Accept *acc ;

#define MAIN_VERSION "1.1.1-test"

int buildConnect(const string& ip, int port){

	int netfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if( netfd < 0 ){
		return -2;
	}

	struct sockaddr_in server_addr;
	bzero(&server_addr,sizeof(struct sockaddr_in));  
    server_addr.sin_family=AF_INET;  
	inet_aton(ip.c_str(), &server_addr.sin_addr);
    //server_addr.sin_addr.s_addr=htonl(INADDR_ANY);  
    server_addr.sin_port = htons(port);  

	int flags = 1;
	if ( setsockopt(netfd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags) ) < 0){
			ERROR_LOG("socket rebind error");
		}

	if(bind(netfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)  
    {  
        ERROR_LOG("error:%d!", errno);  
        return -3;  
    }

	::listen(netfd, 1024);

	flags = ::fcntl(netfd, F_GETFL);  
    flags |= O_NONBLOCK;  
    fcntl(netfd, F_SETFL, flags);  

	
	
    event_set(&acc->netEvent, netfd, EV_READ | EV_PERSIST , dataInput, (void *)acc);  
	event_add(&acc->netEvent, NULL); 
	
	return netfd;

}

int pid_file(const char *file){

	int pidFD = ::open(file, O_CREAT | O_RDWR , S_IRWXU);
	if( pidFD < 0 ){
		ERROR_LOG("file %s is not exist", file);
		return -1;
	}
	pid_t pid = getpid();
	char c_pid[10] = {0};
	snprintf(c_pid, 10, "%d", pid);
	INFO_LOG("pid is in pid.file pid:%s",c_pid);
	int ret = ::write(pidFD, c_pid, 10);
	DEBUG_LOG("debug:%d", ret);
	DEBUG_LOG("debug:%d", errno);
	::close(pidFD);
	return 0;
}

//copy from memcached 
int daemonize(int nochdir, int noclose)
{
    int fd;

    switch (fork()) {
    case -1:
        return (-1);
    case 0:
        break;
    default:
        _exit(EXIT_SUCCESS);
    }

    if (setsid() == -1)
        return (-1);

    if (nochdir == 0) {
        if(chdir("/") != 0) {
            perror("chdir");
            return (-1);
        }
    }

    if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
        if(dup2(fd, STDIN_FILENO) < 0) {
            perror("dup2 stdin");
            return (-1);
        }
        if(dup2(fd, STDOUT_FILENO) < 0) {
            perror("dup2 stdout");
            return (-1);
        }
        if(dup2(fd, STDERR_FILENO) < 0) {
            perror("dup2 stderr");
            return (-1);
        }

        if (fd > STDERR_FILENO) {
            if(close(fd) < 0) {
                perror("close");
                return (-1);
            }
        }
    }
    return (0);
}

//copy from memcache
#ifndef HAVE_SIGIGNORE
static int __sigignore(int sig) {
    struct sigaction sa;
	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;

    if (sigemptyset(&sa.sa_mask) == -1 || sigaction(sig, &sa, 0) == -1) {
        return -1;
    }
    return 0;
}
#endif

int ignoreSignals(){

	if (__sigignore(SIGPIPE) == -1) {
        ERROR_LOG("ignore sig SIGPIPE failed");
        return -1;
    }

	if (__sigignore(SIGHUP) == -1) {
		ERROR_LOG("ignore sig SIGHUP failed");
        return -1;
    }
	return 0;
}

void changeProcessName(int argc, char **argv, string addr){
	
	//change the process info
	string all = "";
	for(int i=0; i<argc; i++){
		if( i == 0) all=(string)argv[i] 
			+ " --main_version:" + (string)MAIN_VERSION
			+ " --port:" + addr
			+ " --backIP:" + GLOBAL::backIP;
		else all = all + " " + (string)argv[i];
	}
	char *processName = argv[0];
	sprintf(processName, "%s", all.c_str());
	
}

int loadDynamicLib(const char *filename){

	return register_plugin(filename);

}

int main(int argc, char **argv){

	INFO_LOG("FProxyServer is created by function");
	main_base = event_init();  
	if( main_base == NULL  ){
		return -1;
	}

	parseIniFile("./proxy.ini");
	string bindip 	= getOptionToString("bindip");
	int port 		= getOptionToInt( "bindport" );
	int backTimeout = getOptionToInt( "backtimeout" );
	string backIP = getOptionToString( "backIP" );
	int logLevel 	= getOptionToInt( "loglevel" );

	if(logInit("./proxy.log", logLevel) < 0){
		printf("init log error\n");
		return -1;
	}
	INFO_LOG("server log init ok!");

	if( backTimeout < 0 ){
		backTimeout = 0;
	}
	
	if( backIP == "" ){
		printf("back ip is NULL!! please check the ini file\n");
		return -2;
	}
	
	GLOBAL::back_timeout = backTimeout;
	GLOBAL::backIP = backIP;
	INFO_LOG("server back timeout :%d ms", GLOBAL::back_timeout);
	
	if( ignoreSignals() < 0){
		return -1;
	}

	if( loadDynamicLib("./test.so") < 0 ){
		return -2;
	}
	
	if (daemonize(1, 1) == -1) {
        fprintf(stderr, "failed to daemon() in order to daemonize\n");
        exit(EXIT_FAILURE);
    }
	
	acc = new Accept();
	acc->setEventBase(main_base);
	INFO_LOG("server bind in %s:%d", bindip.c_str(), port);
	int netfd = buildConnect(bindip, port);
	if( netfd < 0 ){
		ERROR_LOG("%s:%d is already used", bindip.c_str(), port);
		return -2;
	}
	acc->setFd(netfd);

	if( dll.handle_init(argc, argv) < 0){
		printf ("so return error\n");
		return -10;
	}

	char addr[20] = {0};
	snprintf(addr, 20, "%s:%d", bindip.c_str(), port);
	changeProcessName(argc, argv, addr);
	
	pid_file("./pid.file");
	INFO_LOG("###########server is started successed###########");
	event_base_loop(main_base, 0);

	dll.handle_fini();

	cout<<"hello"<<endl;
	return 0;

}	
